package com.linden.std.filter;

import com.alibaba.fastjson.JSON;
import com.linden.std.Utils.UserContextUtil;
import com.linden.std.constant.CommonConstant;
import com.linden.std.domain.vo.UserVO;
import com.linden.std.result.Result;
import com.linden.std.service.auth.LocalAuthService;
import com.linden.std.service.session.SessionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private LocalAuthService authService;
    private SessionService sessionService;
    private final PathMatcher pathMatcher = new AntPathMatcher();
    
    /**
     * 白名单路径
     */
    private static final List<String> WHITE_LIST_PATHS = Arrays.asList(
            "/auth/login",
            "/auth/refresh",
            "/auth/publicKey",
            "/auth/login-security-status",
            "/test/**",
            "/doc.html",
            "/doc.html/**",
            "/swagger-ui.html",
            "/swagger-ui/**",
            "/webjars/**",
            "/v2/api-docs",
            "/v2/api-docs/**",
            "/v3/api-docs",
            "/v3/api-docs/**",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/actuator/health",
            "/favicon.ico",
            "/error"
    );

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                  FilterChain filterChain) throws ServletException, IOException {

        // 延迟初始化authService和sessionService
        if (authService == null) {
            ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
            if (context != null) {
                authService = context.getBean(LocalAuthService.class);
                sessionService = context.getBean(SessionService.class);
            }
        }

        // 1. 跨域处理
        handleCors(response);

        // 2. OPTIONS请求直接通过
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        // 3. 检查是否是白名单路径
        String requestPath = request.getServletPath();
        log.debug("处理请求路径: {}, 请求方法: {}", requestPath, request.getMethod());
        
        if (isWhiteListPath(requestPath)) {
            log.debug("请求路径 {} 在白名单中，直接放行", requestPath);
            filterChain.doFilter(request, response);
            return;
        }

        try {
            // 4. 提取Token
            String token = extractToken(request);
            if (StringUtils.isBlank(token)) {
                handleAuthError(response, "缺少认证Token");
                return;
            }

            // 5. 验证Token并获取用户信息
            if (authService != null) {
                UserVO userVO = authService.validateToken(token);
                if (userVO == null) {
                    handleAuthError(response, "Token无效或已过期");
                    return;
                }

                // 6. 设置用户上下文
                UserContextUtil.setUser(userVO);

                // 7. 从 Redis 刷新用户和权限信息到 Session
                if (sessionService != null) {
                    sessionService.refreshUserAndPermissionToSession(userVO.getUserId(), request.getSession());
                }

                // 8. 设置Spring Security上下文
                setSecurityContext(userVO);
            }

            filterChain.doFilter(request, response);

        } catch (Exception e) {
            log.error("认证过滤器异常", e);
            handleAuthError(response, "认证失败");
        } finally {
            // 只在出现异常或认证失败时清理上下文
            // 正常情况下，让ThreadLocal在请求结束时自然清理
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                UserContextUtil.removeUser();
            }
        }
    }

    private String extractToken(HttpServletRequest request) {
        // 1. 从Header获取
        String bearerToken = request.getHeader(CommonConstant.TOKEN_HEADER);
        log.debug("从请求头获取到的Token: {}", bearerToken);
        
        if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(CommonConstant.TOKEN_PREFIX)) {
            String token = bearerToken.substring(CommonConstant.TOKEN_PREFIX.length());
            log.debug("解析后的Token: {}", token);
            return token;
        }

        // 2. 从Cookie获取
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("accessToken".equals(cookie.getName())) {
                    log.debug("从Cookie获取到Token: {}", cookie.getValue());
                    return cookie.getValue();
                }
            }
        }

        log.debug("未找到Token");
        return null;
    }

    private boolean isWhiteListPath(String requestPath) {
        return WHITE_LIST_PATHS.stream()
                .anyMatch(path -> pathMatcher.match(path, requestPath));
    }

    private void setSecurityContext(UserVO userVO) {
        List<SimpleGrantedAuthority> authorities = userVO.getPermCodes() != null ? 
            userVO.getPermCodes().stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList()) : Collections.emptyList();

        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(userVO.getUserId(), null, authorities);

        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    private void handleAuthError(HttpServletResponse response, String message) throws IOException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=UTF-8");

        Result<Void> result = Result.error(401, message);
        response.getWriter().write(JSON.toJSONString(result));
    }

    private void handleCors(HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
    }
}